
package redeS.p2pwsrep;

import java.util.ArrayList;


import peersim.cdsim.CDState;
import peersim.config.Configuration;
import peersim.core.Control;
import peersim.core.Network;
import redeS.mensagem.GnuMessageProtocol;

/**
 * Classe de controle do protocolo P2PWSREP</br>
 * O que ela faz:</br>
 * 1 - Nos primeiros TTL * 2 + 1 ciclos, inunda a rede com QueryReputations</br>
 * 2 - Nos ciclos seguintes realiza interações entre os nós</br>
 * 3 - No último ciclo ela imprime os resultados.
 * @author thiago-pc
 */
public class ReputationControl implements Control {

	/**
	 * O parâmetro TTL (time-to-leave) representa o número de vezes
	 * que uma mensagem será encaminhada na rede antes de ser excluída.
	 */
	public static final String PAR_TTL = "TTL";
	public static int TTL;
	private final int defaultTTL = 6;
	
	private int pidm = -1;
	
	public ReputationControl(String prefix){
		System.err.println("ReputationControl: Certifique-se que o número de ciclos" +
				" da simulação seja maior que o dobro do TTL");
		TTL = Configuration.getInt(prefix + "." + PAR_TTL, defaultTTL);
		if(Configuration.contains(prefix + ".message_protocol") ){
			pidm = Configuration.getPid(prefix + ".message_protocol");
		}
		if(pidm<0){
			System.err.println("Especifique a string identificadora do protocolo de " +
					"mensagens Gnutella");
			System.exit(1);
		}
	}
	
	@Override
	public boolean execute() {
		if(CDState.getCycle() < TTL * 2 + 1){
			if(CDState.getCycle() == 0){
				System.err.println(getClass().getName() + ": aguardando inundação das " +
						"mensagens de reputação na rede...");
				
				for(int i=0; i<Network.size(); i++){
					GnuMessageProtocol gnuProtocol =
						(GnuMessageProtocol)Network.get(i).getProtocol(pidm);
					
					gnuProtocol.addRequest(new QueryReputation(CDState.r.nextLong(),
							TTL, (P2PWSRepNode)Network.get(i)), i);
				}
			}
			return false;
		}
		else{
			if(CDState.getCycle() == TTL * 2 + 1){
				System.err.println(getClass().getName() + ": realizando interações " +
						"entre os nós...");
			}
			for(int i=0; i<Network.size(); i++){
				P2PWSRepNode currentNode = (P2PWSRepNode)Network.get(i);
				ArrayList<P2PWSRepNode> neighbors = currentNode.getDiscoveredNodes();
				int j = Math.abs(CDState.r.nextInt()) % neighbors.size();
				P2PWSRepNode neighbor = (P2PWSRepNode)Network.get(j);
				int file = Math.abs(CDState.r.nextInt()) % P2PWSRepNode.numberOfFiles;
				boolean trans = neighbor.getNextTrans(file);
				
				//
				//System.out.println("Interação: " + currentNode.getID() + " -> " +
						//neighbor.getID() + " " + trans);
				//
				
				if(trans){
					currentNode.addSatTrans(neighbor);
				}
				else{
					currentNode.addUnsatTrans(neighbor);
				}
			}
			
			// ULTIMO CICLO!!!
			if(CDState.getCycle() == CDState.getEndTime() - 1){
				
				// arrays para cálculo final das reputações
				double reputationSum[] = new double[Network.size()];
				int reputationDiv[] = new int[Network.size()];
				
				for(int i=0;i<68;i++){
					System.out.print('-');
				}
				System.out.println("\n\t\t\t    " + getClass().getSimpleName() + 
						"\n\t\t\t\tResultados");
				for(int i=0;i<68;i++){
					System.out.print('-');
				}
				System.out.println("\n[ID nó]\t\t[rep do nó ID]\t\t     (repNo-repMedRec-prob)\n");
				
				for(int i=0; i<Network.size(); i++){ // para cada nó i da rede
					P2PWSRepNode currentNode = (P2PWSRepNode)Network.get(i);
					// currentNode é um nó K da rede <- nó que quer obter a reputação
					// dos seus vizinhos
					int cSat, cUnsat, size, repSize;
					double rep[];
					
					size = currentNode.getDiscoveredNodes().size();
					rep = new double[size];
					
					
					
					// pega um vizinho J do nó k (currentNode)
					for(int cNode=0; cNode<size; cNode++){
					
					cSat = 0; cUnsat = 0; repSize = 0; // zera variaveis de controle
						
					P2PWSRepNode nj = currentNode.getDiscoveredNodes().get(cNode);
					// nj é o nó J da rede <- nó que terá a reputação consultada
					P2PWSRepNode ni = null;
					// ni é o nó I da rede <- o nó K vai perguntar pra esse nó a respeito do nó J
					
					// esse laço abaixo é para preencher o vetor rep com as reputações do nó
					// J (cNode) perguntada para os nós I (vizinhos de K (currentNode) )
					for(int a=0; a<size; a++){
						// a peercorre os vizinhos de K (currentNode)
						
						if(a==cNode) continue; // discarta a auto-consulta
						
						ni = currentNode.getDiscoveredNodes().get(a); // pega o vizinho
						
						cSat = ni.getSatTrans(nj); // transações satisfatórias
						cUnsat = ni.getUnsatTrans(nj); // transações insatisfatórias
						
						if(cSat == 0 && cUnsat == 0) continue; // discarta qdo não tem informações
						
						rep[repSize] = (double)cSat / (double)(cSat + cUnsat); //preenche o vetor
						repSize++;
					}
					
					double avg = 0;
					for(int k=0; k<repSize; k++){
						avg += rep[k];
					}
					if(repSize > 0){
						avg = avg / repSize;
						currentNode.reputationTable.put(nj, avg);
						System.out.printf("[%5d] obteve a reputação: %.3f de [%5d] (%.3f - %.3f - %.3f)\n",
								currentNode.getID(), avg, nj.getID(),nj.reputation,nj.reputationFileAvg,
								nj.reputation * nj.reputationFileAvg);
						reputationSum[(int) nj.getID()] += avg;
						reputationDiv[(int) nj.getID()]++;
					}
				}
				}
				for(int i=0;i<68;i++){
					System.out.print('-');
				}
				System.out.println("\nRESULTADOS FINAIS");
				System.out.println("Número total de ciclos desta simulação: " +
						CDState.getEndTime());
				System.out.println("Número de ciclos úteis para o cálculo da reputação: " +
						(CDState.getEndTime() - 2*TTL) );
				System.out.println("Time-to-live do QueryReputation: " + TTL);
				double avg = 0;
				for(int i=0; i<Network.size(); i++){
					avg += ((P2PWSRepNode)Network.get(i)).getDiscoveredNodes().size();
				}
				avg = avg / Network.size();
				System.out.printf("Alcance médio do QueryReputation: %.3f nós\n",avg);
				for(int i=0;i<68;i++){
					System.out.print('-');
				}
				System.out.println("\n[ID-NÓ] [repNo - repMR - probS] [REPUT] [ desvio]");
				double desv[] = new double[Network.size()];
				int desvPos = 0;
				
				for(int i=0; i<Network.size(); i++){
					P2PWSRepNode n = ((P2PWSRepNode)Network.get(i));
					if(reputationDiv[i] == 0) continue;
					double probS = n.reputation * n.reputationFileAvg;
					double reput = reputationSum[i]/reputationDiv[i];
					desv[desvPos] = reput - probS;
					
					System.out.printf("[%5d] [%.3f - %.3f - %.3f] [%.3f] [%+.4f]\n",i,
							n.reputation,n.reputationFileAvg,probS,reput,desv[desvPos]);
					
					desvPos++;
				}
				
				double desvAvg = 0;
				for(int i=0; i<desvPos; i++){
					desvAvg += Math.abs(desv[i]);
				}
				desvAvg = desvAvg / desvPos;
				System.out.printf("\nDesvio Médio: %.6f\n\n",desvAvg);
			}
		}
		return false;
	}

}
